using System;
using System.Linq;

namespace Voodoo.Utils
{
	public static class StringMetrics
	{
		private static readonly double mWeightThreshold = 0.7;

		private static readonly int mNumChars = 4;

		public static double JaccardDistance(this string source, string target)
		{
			return 1.0 - source.JaccardIndex(target);
		}

		public static double JaccardIndex(this string source, string target)
		{
			return Convert.ToDouble(source.Intersect(target).Count()) / Convert.ToDouble(source.Union(target).Count());
		}

		public static double LevenshteinDistance(this string source, string target)
		{
			source = source?.Trim();
			target = target?.Trim();
			if (source == null || target == null)
			{
				return 0.0;
			}
			if (source.Length == 0 || target.Length == 0)
			{
				return 0.0;
			}
			if (source == target)
			{
				return 0.0;
			}
			int length = source.Length;
			int length2 = target.Length;
			if (length == 0)
			{
				return length2;
			}
			if (length2 == 0)
			{
				return length;
			}
			int[,] array = new int[length + 1, length2 + 1];
			int num = 0;
			while (num <= length)
			{
				array[num, 0] = num++;
			}
			int num2 = 0;
			while (num2 <= length2)
			{
				array[0, num2] = num2++;
			}
			for (int i = 1; i <= length; i++)
			{
				for (int j = 1; j <= length2; j++)
				{
					int num3 = ((target[j - 1] != source[i - 1]) ? 1 : 0);
					array[i, j] = Math.Min(Math.Min(array[i - 1, j] + 1, array[i, j - 1] + 1), array[i - 1, j - 1] + num3);
				}
			}
			return array[length, length2];
		}

		public static double NormalizedLevenshteinDistance(this string source, string target)
		{
			return source.LevenshteinDistance(target) / (double)Math.Max(source.Length, target.Length);
		}

		public static double NormalizedLevenshteinProximity(this string source, string target)
		{
			double num = source.LevenshteinDistance(target);
			return 1.0 - num / (double)Math.Max(source.Length, target.Length);
		}

		public static double OverlapCoefficient(this string source, string target)
		{
			return Convert.ToDouble(source.Intersect(target).Count()) / Convert.ToDouble(Math.Min(source.Length, target.Length));
		}

		public static double JaroWinklerDistance(this string aString1, string aString2)
		{
			return 1.0 - aString1.JaroWinklerProximity(aString2);
		}

		public static double JaroWinklerProximity(this string aString1, string aString2)
		{
			int length = aString1.Length;
			int length2 = aString2.Length;
			if (length == 0)
			{
				if (length2 != 0)
				{
					return 0.0;
				}
				return 1.0;
			}
			int num = Math.Max(0, Math.Max(length, length2) / 2 - 1);
			bool[] array = new bool[length];
			bool[] array2 = new bool[length2];
			int num2 = 0;
			for (int i = 0; i < length; i++)
			{
				int num3 = Math.Max(0, i - num);
				int num4 = Math.Min(i + num + 1, length2);
				for (int j = num3; j < num4; j++)
				{
					if (!array2[j] && aString1[i] == aString2[j])
					{
						array[i] = true;
						array2[j] = true;
						num2++;
						break;
					}
				}
			}
			if (num2 == 0)
			{
				return 0.0;
			}
			int num5 = 0;
			int k = 0;
			for (int l = 0; l < length; l++)
			{
				if (array[l])
				{
					for (; !array2[k]; k++)
					{
					}
					if (aString1[l] != aString2[k])
					{
						num5++;
					}
					k++;
				}
			}
			int num6 = num5 / 2;
			double num7 = num2;
			double num8 = (num7 / (double)length + num7 / (double)length2 + (double)(num2 - num6) / num7) / 3.0;
			if (num8 <= mWeightThreshold)
			{
				return num8;
			}
			int num9 = Math.Min(mNumChars, Math.Min(aString1.Length, aString2.Length));
			int m;
			for (m = 0; m < num9 && aString1[m] == aString2[m]; m++)
			{
			}
			if (m == 0)
			{
				return num8;
			}
			return num8 + 0.1 * (double)m * (1.0 - num8);
		}
	}
}
